/**
 * SNRI R2 - Standard Network Routing Interface Revision 2
 * Loadable module header 
 * 
 * Tyson Malchow 2008
 */
#ifndef SNRI_MODULE_H_
#define SNRI_MODULE_H_

#include <stdarg.h>
#include <sys/time.h>
#include <netinet/in.h>

/* snricall execution codes */
#define SNRICALL_GETIP 1
#define SNRICALL_DATAGRAM_SOCKET 2
#define SNRICALL_DATAGRAM_SEND 3
#define SNRICALL_LOG 4
#define SNRICALL_LOG_BINARY 5
#define SNRICALL_CLOSE 6
#define SNRICALL_DIE 7
#define SNRICALL_CREATE_TIMER 8
#define SNRICALL_DESTROY_TIMER 9
#define SNRICALL_GETTIME 10
#define SNRICALL_REGISTER_GLOBALS 11
#define SNRICALL_MEM_FREE 12
#define SNRICALL_MEM_MALLOC 13
#define SNRICALL_MEM_CALLOC 14
#define SNRICALL_MEM_REALLOC 15

/* snricall function pointer */
typedef int (*_snricall_fpref_t)(int, ...);
extern _snricall_fpref_t _snricall_fpref;
#define _snricall (*_snricall_fpref)

/* SNRI module callbacks */
void _snri_mod_instantiate(int argc, char * argv[], in_addr_t);
void _snri_mod_init();
void _snri_mod_uninit();
void _snri_mod_destroy();


/* a couple callback types */
typedef void (*snri_timer_callback_fp)(
			int,  			/* timer id */
			void * 			/* saved argument */
		);
typedef void (*snri_network_message_callback_fp)(
			int, 			/* socket */
			in_addr_t, 		/* source address*/
			int, 			/* source port*/
			unsigned int, 	/* data length */
			char * 			/* data */
		);

/**
 * Error code return value for all snri_* functions
 *
 */
#define SNRI_ERROR -1

/**
 * Returns all IPs available to this node 
 * 
 * @param addrs Output parameter for the in_addr_t array
 * @return Number of addresses assigned
 */
static inline int snri_getips(in_addr_t ** addrs) {
	int num_addrs;
	
	if(_snricall(SNRICALL_GETIP, &num_addrs, addrs))
		return num_addrs;
	else
		return SNRI_ERROR;
}

/**
 * Returns the first IP in the list given by snri_getips()
 * 
 * @return IP of this node
 */
static inline in_addr_t snri_getip() {
	in_addr_t * addrs;
	
	if(snri_getips(&addrs) > 0 && addrs)
		return addrs[0];
	else
		return SNRI_ERROR;
}


/**
 * Creates a datagram socket. 
 * You must have created a socket for any SNRI communications
 * 
 * @return the socket identifier
 */ 
static inline int snri_datagram_socket(in_addr_t bind_address, int port, snri_network_message_callback_fp callback_function) {
	int socket_id;
	if(_snricall(SNRICALL_DATAGRAM_SOCKET, bind_address, port, callback_function, &socket_id))
		return socket_id;
	else
		return SNRI_ERROR;
}

/**
 * Sends a datagram to a given destination using a datagram socket
 */
static inline int snri_datagram_send(int datagram_socket, in_addr_t dest_addr, int dest_port, unsigned int data_length, char * data) {
	return _snricall(SNRICALL_DATAGRAM_SEND, datagram_socket, dest_addr, dest_port, data_length, data) ? 0 : SNRI_ERROR;
}

/**
 * @param t Will be filled with the current system time
 */
static inline int snri_gettime(struct timeval *t) {
	return _snricall(SNRICALL_GETTIME, t);
}

/**
 * @param socket Socket that will be closed
 */
static inline int snri_close(int socket) {
	return _snricall(SNRICALL_CLOSE, socket) ? 0 : SNRI_ERROR;
}

/**
 * Creates a timer that will expire after the given delay
 * 
 * @param milli_delay Number of milliseconds after which this timer will expire
 * @param callback_function The function that will be called when this timer expires
 */
static inline int snri_timer_create(int milli_delay, snri_timer_callback_fp callback_function, void * cb_arg) {
	int timer_id;
	if(_snricall(SNRICALL_CREATE_TIMER, milli_delay, callback_function, cb_arg, &timer_id))
		return timer_id;
	else
		return SNRI_ERROR;
}

/**
 * Destroys the timer with the given ID (prevents it from executing '
 */
static inline int snri_timer_destroy(int timer_id) {
	return _snricall(SNRICALL_DESTROY_TIMER, timer_id) ? 0 : SNRI_ERROR;
}

/**
 * Schedules this node for deletion
 */
static inline int snri_die() {
	return _snricall(SNRICALL_DIE) ? 0 : SNRI_ERROR;
}

/**
 * Logs some message
 */
static inline int snri_log(char * fmt, ...) {
	/*_snricall(SNRICALL_LOG);*/
  return 0;
}

/**
 * Logs some binary data
 */
static inline int snri_log_binary(char * data, int data_size) {
	/*_snricall(SNRICALL_LOG_BINARY);*/
  return 0;
}

/**
 * Registers the set of globals for this module.
 * This should be done when the module is created and gets the _snri_mod_init function called 
 * 
 * @param num_globals Number of globals to register
 * The rest of the params should follow this form:
 * 		global variable pointer
 * 		size of data
 * 
 * For instance, you may have the following globals:
 *   int a; char b;
 * 
 * So, you'd call:
 *   
 *  snri_regsiter_globals(2, &a, sizeof(a), &b, sizeof(b));
 * 
 * Simple!
 */
#define snri_register_globals(...) _snricall(SNRICALL_REGISTER_GLOBALS, __VA_ARGS__)


#endif /*SNRI_MODULE_H_*/
